#include "hzpch.h"
#include "Application.h"

#include "Hazel/Core/log.h"
#include "Hazel/Core/Input.h"

#include "Hazel/Events/Event.h"

#include "Hazel/Renderer/Renderer.h"

#include "Hazel/ImGui/ImGuiLayer.h"

#include <GLFW/glfw3.h>
namespace Hazel {
#define BIND_EVENT_FN(x) std::bind(&x, this, std::placeholders::_1)
	Application* Application::s_Instance = nullptr;


	Application::Application()
	{
		HZ_PROFILE_FUNCTION();

		HZ_CORE_ASSERT(!s_Instance, "Application already exists!");
		s_Instance = this;

		m_Window = std::unique_ptr<Window>(Window::Create());
		m_Window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));
		m_Window->SetVSync(true);

		Renderer::Init();

		m_ImGuiLayer = new ImGuiLayer;
		PushOverLayer(m_ImGuiLayer);

	}

	void Application::OnEvent(Event& e)
	{
		EventDispatcher dispatcher(e);
		dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(Application::OnWindowClose));
		dispatcher.Dispatch<WindowResizeEvent>(BIND_EVENT_FN(Application::OnWindowResize));
		//HZ_TRACE("{0}", e.ToString());


		for (Layer* layer : m_LayerStack)
		{
			layer->OnEvent(e);
			if (e.GetHandled())
				break;

		}
	}
	void Application::Close()
	{
		m_Running = false;
	}
	void Application::PushLayer(Layer* layer)
	{
		m_LayerStack.PushLayer(layer);
		layer->OnAttach();
	}
	void Application::PushOverLayer(Layer* overlayer)
	{
		m_LayerStack.PushOverlay(overlayer);
		overlayer->OnAttach();
	}
	Application::~Application()
	{

	}
	void Application::Run()
	{

		while (m_Running)
		{
			Hazel::RenderCommand::SetClearColor({ 0.3f, 0.3f, 0.3f, 1 });
			Hazel::RenderCommand::Clear();

			float time = glfwGetTime();
			m_Timestep = time - m_LastFrameTime;
			m_LastFrameTime = time;


			if (!m_Minimized)
			{
				for (Layer* layer : m_LayerStack)
					layer->OnUpdate(m_Timestep);

			}

			m_ImGuiLayer->Begin();
			for (Layer* layer : m_LayerStack)
				layer->OnImGuiRender();
			m_ImGuiLayer->End();

			m_Window->OnUpdate();
		}
	}

	bool Application::OnWindowClose(WindowCloseEvent& e)
	{
		m_Running = false;
		return true;
	}
	bool Application::OnWindowResize(WindowResizeEvent& e)
	{
		if (e.GetWidth() == 0 || e.GetHeight() == 0)
		{
			m_Minimized = true;
			return false;
		}
		m_Minimized = false;

		Renderer::OnWindowResize(e.GetWidth(), e.GetHeight());


		return false;
	}
}